/**
 * vk-uni-pay 统一支付
 * 请勿修改此处代码，因为插件更新后此处代码会被覆盖。
 * 作者：VK
 * 发布于：2021-07-06
 */
"use strict";

const crypto = require("crypto");

const configCenter = require("uni-config-center");

const config = configCenter({ pluginId: "uni-pay" }).requireFile("config.js");

const dao = require("./dao");

const libs = require("./libs");

const openapi = require("./openapi");

const uniPay = libs.uniPay;

var vkPay = {};

vkPay.libs = libs;
vkPay.request = libs.common.request;
vkPay.crypto = libs.crypto;

const notifyPath = "/vk-pay-notify/";
const transferNotifyPath = "/transfer-notify/";

const db = uniCloud.database();
const _ = db.command;

/**
 * 获取支付插件的配置（如果不传参数，则获取到的是完整配置）
 * 特别注意：请不要将密钥信息返回给前端，防止密钥泄露。
 * 特别注意：请不要将密钥信息返回给前端，防止密钥泄露。
 * 特别注意：请不要将密钥信息返回给前端，防止密钥泄露。
 * 只在云函数中调用和使用，且结果不return给前端，则是安全的。
 */
vkPay.getConfig = (key) => {
	if (key) {
		return libs.common.getData(config, key);
	} else {
		return config;
	}
};

/**
 * 支付成功 - 异步通知
 */
vkPay.paymentNotify = async (obj = {}) => {
	let { event, context, orderPaySuccess } = obj;
	let startTime = Date.now();
	console.log("原始回调数据: ", event);
	let notifyPathSplit = event.path.substring(notifyPath.length).split("/");
	let pay_type = notifyPathSplit[0]; // 获取支付方式
	let out_trade_no = notifyPathSplit[1]; // 获取商户支付订单号
	let provider_pay_method = notifyPathSplit[2]; // 渠道支付下的支付方式
	if (!out_trade_no) {
		// 如果没有在path中获得商户支付订单号，则从回调数据中获取
		let notifyData = libs.common.getNotifyData(event, pay_type);
		out_trade_no = notifyData.out_trade_no;
	}
	// 根据out_trade_no获取支付订单信息
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		console.log("---------!支付订单信息不存在!---------");
		console.log("---------!支付订单信息不存在!---------");
		console.log("---------!支付订单信息不存在!---------");
		return libs.common.returnNotifySUCCESS(pay_type);
	}
	if (!pay_type) pay_type = payOrderInfo.pay_type;
	if (payOrderInfo.pay_type !== pay_type) payOrderInfo.pay_type = pay_type;
	if (provider_pay_method)
		payOrderInfo.provider_pay_method = provider_pay_method;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	let notifyType = await uniPayInstance.checkNotifyType(event);
	console.log("notifyType：", notifyType);
	// 如果uniPayInstance中有returnNotifySuccess方法，则使用uniPayInstance中的方法，否则使用common中的方法
	let returnNotifySuccess =
		typeof uniPayInstance.returnNotifySuccess === "function"
			? uniPayInstance.returnNotifySuccess
			: libs.common.returnNotifySUCCESS;
	// 微信小程序虚拟支付特殊处理开始-----------------------------------------------------------
	if (notifyType === "token") {
		let verifyResult = await uniPayInstance.verifyTokenNotify(event);
		console.log("verifyResult: ", verifyResult);
		if (!verifyResult) {
			console.log("---------!token签名验证未通过!---------");
			return;
		}
		// 校验token的测试接口，直接返回echostr
		return verifyResult.echostr;
	}
	// 微信小程序虚拟支付特殊处理结束-----------------------------------------------------------
	if (notifyType !== "payment") {
		// 由于支付宝部分退款会触发支付成功的回调，但同时签名验证是算未通过的，为了避免支付宝重复推送，这里可以直接返回成功告知支付宝服务器，不用再推送过来了。
		console.log(`---------!非支付通知!---------`);
		return returnNotifySuccess(pay_type);
	}
	let verifyResult = await uniPayInstance.verifyPaymentNotify(event);
	if (!verifyResult) {
		console.log("---------!签名验证未通过!---------");
		console.log("---------!签名验证未通过!---------");
		console.log("---------!签名验证未通过!---------");
		// 这里返回成功，是为了避免第三方支付服务器重复推送
		return returnNotifySuccess(pay_type);
	}
	console.log("---------!签名验证通过!---------");
	verifyResult = JSON.parse(JSON.stringify(verifyResult)); // 这一句代码有用，请勿删除。
	console.log("verifyResult: ", verifyResult);
	let {
		outTradeNo, // 商户支付订单号
		totalFee, // 订单金额
		transactionId, // 第三方支付单号
		tradeState, // 微信支付v3判断支付成功的字段
		resultCode, // 微信支付v2和支付宝支付判断成功的字段
		returnCode,
		mchId, // 商户id
		openid, // 支付用户的openid
		appId,
		authAppId,
		sellerId,
		payMethod, // 抖音和其他渠道支付专属支付
	} = verifyResult;
	if (!mchId && sellerId) mchId = sellerId;

	if (
		returnCode == "SUCCESS" ||
		resultCode == "SUCCESS" ||
		tradeState === "SUCCESS"
	) {
		// 调用支付成功统一处理函数
		await vkPay.paymentSuccess({
			notify_mode: 1, // 通知模式：0 主动调用 1 异步回调
			out_trade_no: outTradeNo, // 商户支付订单号
			transaction_id: transactionId, // 第三方支付单号
			// 以下是额外修改的数据
			extra_data: {
				pay_type, // 更新支付方式（重要）
				notify_info: verifyResult, // 存本次回调的通知数据
				original_data: event, // 存本次的回调原始数据（为了留个备份，方便核查数据）
				provider_pay_method: provider_pay_method || payMethod,
				provider_appid: appId,
				mchId, // 商户id
				openid, // 支付用户的openid
				auth_appid: authAppId,
				seller_id: sellerId,
			},
			// 用户自己的回调处理函数
			success: orderPaySuccess,
		});
	} else {
		console.log("未检测到支付成功的状态字段，verifyResult:", verifyResult);
	}
	// 给第三方服务器回复 开始-----------------------------------------------------------
	let endTime = Date.now();
	let runTime = endTime - startTime;
	console.log(`回调函数 - 全部执行完成，总耗时：${runTime}毫秒`);
	if (runTime > 4000) {
		console.log(
			"---------!您的回调执行时间过长，建议优化自定义回调逻辑，建议控制在4000毫秒以内---------!"
		);
		console.log(
			"---------!您的回调执行时间过长，建议优化自定义回调逻辑，建议控制在4000毫秒以内---------!"
		);
		console.log(
			"---------!您的回调执行时间过长，建议优化自定义回调逻辑，建议控制在4000毫秒以内---------!"
		);
	}
	return returnNotifySuccess(pay_type);
	// 给第三方服务器回复 结束-----------------------------------------------------------
};

/**
 * 支付成功统一处理函数
 * 同一个商户支付订单号，只有首次调用此函数时才会执行用户自定义回调函数
 * @param {String} out_trade_no    商户支付订单号
 * @param {Object} transaction_id  第三方支付单号
 * @param {Object} extra_data     额外修改的数据
 * @param {Function} success       成功后执行的自定义函数
 */
vkPay.paymentSuccess = async (obj = {}) => {
	let {
		type, // 回调函数类型
		out_trade_no,
		transaction_id,
		extra_data = {},
		success: orderPaySuccess,
		notify_mode = 0, // 通知模式：0 主动调用 1 异步回调
	} = obj;
	if (!out_trade_no) {
		return false;
	}
	// 如果未传 orderPaySuccess 函数，则尝试加载自定义异步回调函数（正常只有主动回调时才没有orderPaySuccess函数）
	if (typeof orderPaySuccess !== "function" && type) {
		// 加载自定义异步回调函数
		try {
			orderPaySuccess = require(`../../service/pay-notify/${type}`);
		} catch (err) {
			try {
				orderPaySuccess = require(`../../vk-pay/service/pay-notify/${type}`);
			} catch (err2) {
				console.log(err);
			}
		}
	}
	console.log("支付成功统一处理函数 - 开始执行");
	let nowTime = Date.now();
	// 只有首次推送才执行用户自己的回调函数。
	let payOrderInfo = await dao.payOrders.updateAndReturn({
		whereJson: {
			status: 0, // status:0 为必须条件，防止重复推送
			out_trade_no, // 商户支付订单号
		},
		dataJson: {
			status: 1, // 设置为已付款
			transaction_id, // 更新第三方支付单号
			pay_date: nowTime, // 更新付款时间（这里以通知时间作为付款时间）
			notify_date: nowTime, // 更新通知时间
			notify_num: _.inc(1), // 通知次数，这里只会是0或1
			notify_mode, // 更新通知模式 0 主动调用 1 异步回调
			...extra_data,
		},
	});
	// if (!payOrderInfo) {
	// 	payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	// 	// 如果之前是主动调用，本次是异步回调，则更新数据
	// 	if (payOrderInfo && payOrderInfo.notify_mode === 0 && notify_mode === 1) {
	// 		await dao.payOrders.updateById(payOrderInfo._id, {
	// 			transaction_id, // 更新第三方支付单号
	// 			notify_mode, // 更新通知模式
	// 			...extra_data,
	// 		});
	// 		console.log(
	// 			"---------！注意：该订单已主动执行过回调函数，故本次不执行用户自定义回调函数！---------"
	// 		);
	// 	} else {
	// 		console.log(
	// 			"---------！注意：本次回调非首次回调，已被插件拦截，插件不会执行你的回调函数！---------"
	// 		);
	// 		console.log(
	// 			"---------！注意：本次回调非首次回调，已被插件拦截，插件不会执行你的回调函数！---------"
	// 		);
	// 		console.log(
	// 			"---------！注意：本次回调非首次回调，已被插件拦截，插件不会执行你的回调函数！---------"
	// 		);
	// 	}
	// 	console.log("支付成功统一处理函数 - 执行完成");
	// 	return false;
	// }
	// 用户自己的回调函数 开始-----------------------------------------------------------
	let userOrderSuccess = false;
	let userOrderErrMsg;
	if (typeof orderPaySuccess === "function") {
		console.log("用户自己的回调函数 - 开始执行");
		// 让异步回调和主动回调保持一致的payOrderInfo传给自定义回调函数（过滤掉notify_info和original_data，这两个只有异步回调才有，自定义回调不应该依赖这2个字段）
		delete payOrderInfo.notify_info;
		delete payOrderInfo.original_data;
		let notifyModeArr = ["主动调用", "异步回调"];
		console.log("本次回调模式：", notifyModeArr[notify_mode]);
		console.log("type：", payOrderInfo.type);
		console.log("data：", payOrderInfo);
		try {
			let orderPaySuccessRes = await orderPaySuccess(
				{
					data: payOrderInfo,
				},
				{ db, _ }
			);
			if (typeof orderPaySuccessRes === "string") {
				userOrderSuccess = false;
				userOrderErrMsg = orderPaySuccessRes;
			} else {
				userOrderSuccess = orderPaySuccessRes;
			}
		} catch (err) {
			console.error(err);
			userOrderSuccess = false;
			userOrderErrMsg = err.stack;
		}
		console.log("用户自己的回调函数 - 执行完成");
	}
	console.log("userOrderSuccess", userOrderSuccess);
	// 用户自己的回调函数 结束-----------------------------------------------------------
	try {
		await dao.payOrders.update({
			whereJson: {
				status: 1,
				out_trade_no,
			},
			dataJson: {
				user_order_success: userOrderSuccess,
				user_order_err_msg: userOrderSuccess ? _.remove() : userOrderErrMsg,
			},
		});
	} catch (err) {
		console.error("存本次的回调数据异常：", err);
	}
	console.log("支付成功统一处理函数 - 执行完成");
	return true;
};

/**
 * 统一支付 - 创建支付
 * @param {String} provider    供应商:wxpay、alipay
 * @param {Object} context     请求上下文
 * @param {Object} data        订单参数
 * data参数
 * @param {String} openid  		用户openid，小程序支付时必传
 * @param {String} out_trade_no  商户支付订单号
 * @param {Number} total_fee    订单金额(单位分 100 = 1元)
 * @param {String} subject     订单标题
 * @param {String} body        订单详情
 * @param {String} type        订单类型 goods：订单付款 recharge：余额充值付款 vip：vip充值付款 等等，可自定义
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 * @param {String} out_trade_no 商户支付订单号
 * @param {Object} orderInfo  支付订单信息
 */
vkPay.createPayment = async (obj = {}) => {
	let {
		provider,
		provider_pay_method,
		context,
		alipayAppPayToH5Pay: myAlipayAppPayToH5Pay,
		needQRcode,
		isPC,
		data = {},
	} = obj;
	if (!context) context = uniCloud.$context;
	let {
		SPACEINFO,
		PLATFORM,
		CLIENTUA = "",
		CLIENTIP,
		deviceId,
		appId: dcloudAppid,
	} = context;
	let {
		pid, // 商户id，若此参数有值，则会从数据库中获取支付配置进行支付
		app_auth_token, // 支付宝服务商模式下，子商户的授权token，若该参数有值，自动视为支付宝服务商模式
		sub_appid, // 微信支付服务商模式下，子商户的appid，若该参数有值，自动视为微信支付服务商模式
		sub_mchid, // 微信支付服务商模式下，子商户的mchid，与sub_appid搭配使用
		openid,
		out_trade_no,
		total_fee,
		subject,
		body,
		passback_params,
		type,
		user_id,
		nickname,
		return_url,
		time_expire, // 订单绝对超时时间（13位时间戳格式）
		profit_sharing,
		wxpay_virtual, // 仅微信虚拟支付生效
		auth_code, // 付款码
		store_id, // 付款码机构id
		biz_type, // 仅华为支付生效，且必传
		custom,
		other,
	} = data;
	// 如果设置了用户白名单，则只允许白名单中的用户调用支付接口
	if (
		config.userWhitelist &&
		config.userWhitelist.length > 0 &&
		config.userWhitelist[0] &&
		config.userWhitelist.indexOf(user_id) === -1
	) {
		return new libs.error({ code: 4001 });
	}
	if (!out_trade_no || typeof out_trade_no !== "string") {
		return new libs.error({ code: 1001 });
	}
	if (!type || typeof type !== "string") {
		return new libs.error({ code: 1002 });
	}
	if (provider === "wxpay-virtual") {
		if (typeof wxpay_virtual !== "object") {
			return new libs.error({ code: 1017 });
		}
		if (
			typeof wxpay_virtual.buy_quantity !== "number" ||
			wxpay_virtual.buy_quantity <= 0 ||
			wxpay_virtual.buy_quantity % 1 !== 0
		) {
			return new libs.error({ code: 1018 });
		}
	} else {
		if (
			typeof total_fee !== "number" ||
			total_fee <= 0 ||
			total_fee % 1 !== 0
		) {
			return new libs.error({ code: 1003 });
		}
	}

	if (!subject || typeof subject !== "string") {
		return new libs.error({ code: 1004 });
	}
	if (!provider || typeof provider !== "string") {
		return new libs.error({ code: 1005 });
	}
	if (["wxpay", "douyin"].indexOf(provider) > -1 && !body) body = subject;
	let nowTime = Date.now();
	let res = { code: 0, msg: "ok", out_trade_no };
	// 获取支付配置
	const { notifyUrl, sysServiceProviderId = "2088731216435275" } = config;
	let { alipayAppPayToH5Pay } = config;
	if (typeof myAlipayAppPayToH5Pay !== "undefined")
		alipayAppPayToH5Pay = myAlipayAppPayToH5Pay;
	// 业务逻辑开始-----------------------------------------------------------
	let spaceId = SPACEINFO.spaceId; // 服务空间ID
	let currentNotifyUrl = notifyUrl[spaceId]; // 异步回调地址
	// 兼容正式版阿里云与公测版阿里云spaceId开始-----------------------------------------------------------
	if (!currentNotifyUrl) {
		if (spaceId.indexOf("mp-") === 0) {
			currentNotifyUrl = notifyUrl[spaceId.substring(3)];
		} else {
			currentNotifyUrl = notifyUrl[`mp-${spaceId}`];
		}
	}
	// 兼容正式版阿里云与公测版阿里云spaceId结束-----------------------------------------------------------
	if (!currentNotifyUrl || currentNotifyUrl.indexOf("http") !== 0) {
		return new libs.error({ code: 101 });
	}
	PLATFORM = libs.common.getPlatform(PLATFORM);
	// 如果需要二维码支付模式，则强制定义以下参数
	if (needQRcode) {
		isPC = true;
		PLATFORM = "h5";
		openid = "";
		res.needQRcode = needQRcode;
	}
	if (PLATFORM === "h5" && provider === "alipay") {
		// 支付宝不需要openid
		openid = "";
	}
	if (
		PLATFORM === "h5" &&
		provider === "wxpay" &&
		libs.common.isMobile(CLIENTUA, isPC)
	) {
		if (CLIENTUA.toLowerCase().indexOf("micromessenger") == -1) {
			// 微信手机外部浏览器支付
			PLATFORM = "mweb";
		} else {
			// 微信公众号
			PLATFORM = "h5-weixin";
			if (!openid) {
				return new libs.error({ code: 1006 });
			}
		}
	}
	if (provider === "appleiap") {
		PLATFORM = "app-plus";
	}
	if (auth_code) {
		// 付款码支付
		PLATFORM = "codepay";
	}
	// 如果是华为支付，且是app-plus，则强制切换为app-harmony
	if (provider === "huawei" && PLATFORM === "app-plus") {
		PLATFORM = "app-harmony";
	}
	let pay_type = provider + "_" + PLATFORM; // 支付方式
	if (pid) {
		alipayAppPayToH5Pay = await vkPay.getAlipayAppPayToH5Pay({ pid });
	}
	// 此处是为了让支付宝app支付直接调用支付宝当面付支付，这样不用去申请APP申请接口权限，只需要申请支付宝当面付接口即可
	if (alipayAppPayToH5Pay && pay_type == "alipay_app-plus")
		pay_type = "alipay_h5";
	// 拼接实际异步回调地址（在路径上体现了支付方式）
	let currentNotifyPath = notifyPath + pay_type + "/" + out_trade_no;
	if (provider_pay_method) currentNotifyPath += "/" + provider_pay_method;
	let finalNotifyUrl = currentNotifyUrl + currentNotifyPath;
	// 初始化uniPayInstance
	let uniPayConifg = await vkPay.getUniPayConfig({
		pay_type,
		pid,
		app_auth_token,
		sub_appid,
		sub_mchid,
		provider_pay_method,
	});
	let uniPayInstance = await vkPay.initUniPayInstance({
		pay_type,
		pid,
		app_auth_token,
		sub_appid,
		sub_mchid,
		provider_pay_method,
	});
	let tradeType = vkPay.getTradeType(pay_type);
	// 获取支付信息
	let getOrderInfoParam = {
		subject: subject,
		body: body,
		outTradeNo: out_trade_no,
		totalFee: total_fee,
		notifyUrl: finalNotifyUrl,
		tradeType: tradeType,
	};
	// 只有传了openid，且有值，参数才带上openid
	if (openid) {
		getOrderInfoParam.openid = openid;
	}
	// 微信支付外部浏览器支付特殊处理
	if (pay_type === "wxpay_mweb") {
		getOrderInfoParam.spbillCreateIp = CLIENTIP;
		if (uniPayConifg.version === 3) {
			// v3版本特殊处理
			getOrderInfoParam.sceneInfo = JSON.parse(
				JSON.stringify(uniPayConifg.sceneInfo)
			);
			if (getOrderInfoParam.sceneInfo.h5_info.wap_url) {
				getOrderInfoParam.sceneInfo.h5_info.app_url =
					getOrderInfoParam.sceneInfo.h5_info.wap_url;
				delete getOrderInfoParam.sceneInfo.h5_info.wap_url;
			}
			if (getOrderInfoParam.sceneInfo.h5_info.wap_name) {
				getOrderInfoParam.sceneInfo.h5_info.app_name =
					getOrderInfoParam.sceneInfo.h5_info.wap_name;
				delete getOrderInfoParam.sceneInfo.h5_info.wap_name;
			}
		} else {
			// v2版本
			getOrderInfoParam.sceneInfo = uniPayConifg.sceneInfo;
		}
	}
	// 如果是vkspay，则带上returnUrl
	if (provider === "vkspay" && return_url) {
		getOrderInfoParam.returnUrl = return_url;
	}
	// 如果是自定义渠道支付，带上provider_pay_method等参数
	if (uniPayConifg.custom) {
		if (!body) getOrderInfoParam.body = subject;
		if (return_url) getOrderInfoParam.returnUrl = return_url;
		if (provider_pay_method) getOrderInfoParam.payMethod = provider_pay_method;
		getOrderInfoParam.spbillCreateIp = CLIENTIP;
		getOrderInfoParam.platform = PLATFORM;
	}
	if (time_expire) {
		// 获取各支付供应商对应的timeExpire格式
		getOrderInfoParam.timeExpire = libs.common.getTimeExpire({
			time_expire,
			provider,
			version: uniPayConifg.version,
		});
	}
	if (provider === "huawei") {
		getOrderInfoParam.bizType = biz_type || "100002";
	}
	res.pay_type = pay_type;
	res.provider = provider;
	res.provider_pay_method = provider_pay_method;
	res.total_fee = total_fee;
	res.platform = PLATFORM;
	// 第三方支付服务器返回的订单信息
	let orderInfo;
	let expand_data;
	try {
		// 如果是苹果内购，不需要执行uniPayInstance.getOrderInfo等操作
		if (provider !== "appleiap") {
			if (provider === "alipay") {
				// 支付宝支付特殊参数
				if (typeof data.extendParams === "undefined" && sysServiceProviderId) {
					getOrderInfoParam.extendParams = { sysServiceProviderId };
				}
				if (uniPayConifg.sysServiceProviderId) {
					getOrderInfoParam.extendParams = {
						sysServiceProviderId: uniPayConifg.sysServiceProviderId,
					};
				}
				if (passback_params) {
					// 支付宝需要进行encodeURIComponent编码
					getOrderInfoParam.passbackParams = encodeURIComponent(
						JSON.stringify(passback_params)
					);
				}
				// 判断是否需要分账（订单资金冻结）
				if (profit_sharing) {
					if (typeof data.extendParams === "undefined") data.extendParams = {};
					getOrderInfoParam.extendParams.royalty_freeze = true;
				}
				// 支付宝小程序特殊处理
				if (pay_type === "alipay_mp-alipay" && uniPayConifg.productCode) {
					getOrderInfoParam.productCode = uniPayConifg.productCode;
				}
			} else if (provider === "wxpay") {
				// 微信支付特殊参数
				if (passback_params) {
					getOrderInfoParam.attach = JSON.stringify(passback_params);
				}
				// 判断是否需要分账（订单资金冻结）
				if (profit_sharing) {
					if (uniPayConifg.version === 3) {
						getOrderInfoParam.settle_info = {
							profit_sharing: true,
						};
					} else {
						getOrderInfoParam.profit_sharing = "Y";
					}
				}
			} else if (provider === "wxpay-virtual") {
				// 微信虚拟支付扩展数据
				expand_data = {
					mode: wxpay_virtual.mode, // short_series_coin 代币充值; short_series_goods 道具直购
					buy_quantity: wxpay_virtual.buy_quantity,
					rate: uniPayConifg.rate || 100,
					sandbox: uniPayConifg.sandbox,
				};
				if (wxpay_virtual.mode === "short_series_goods") {
					expand_data.product_id = wxpay_virtual.product_id;
					expand_data.goods_price = wxpay_virtual.goods_price;
				}
				// 获取用户的sessionKey
				let { session_key } = await dao.opendbOpenData.getSessionKey({
					appId: uniPayConifg.appId,
					platform: "weixin-mp",
					openid: openid,
				});
				getOrderInfoParam.sessionKey = session_key;
				getOrderInfoParam.mode = expand_data.mode;
				getOrderInfoParam.buyQuantity = expand_data.buy_quantity;
				getOrderInfoParam.productId = expand_data.product_id;
				getOrderInfoParam.goodsPrice = expand_data.goods_price;
				if (getOrderInfoParam.mode === "short_series_coin") {
					// 计算支付金额
					total_fee =
						(expand_data.buy_quantity / (expand_data.rate || 100)) * 100;
				} else if (getOrderInfoParam.mode === "short_series_goods") {
					// 计算支付金额
					total_fee = expand_data.buy_quantity * expand_data.goods_price;
				} else {
					return new libs.error({ code: 1019 });
				}
			}
			if (other) {
				other = libs.common.snake2camelJson(other);
				getOrderInfoParam = Object.assign(getOrderInfoParam, other);
			}
			if (auth_code) {
				// 付款码支付
				// 一个付款码只能付款一个订单
				let payOrderInfo = await dao.payOrders.find({
					auth_code,
				});
				if (payOrderInfo && payOrderInfo.out_trade_no !== out_trade_no) {
					return new libs.error({ code: 1020 });
				}
				getOrderInfoParam.authCode = auth_code;
				getOrderInfoParam.store_id = store_id;
			} else {
				// 统一下单
				res.orderInfo = await uniPayInstance.getOrderInfo(getOrderInfoParam);
			}
			if (typeof res.orderInfo === "object") {
				// 支付宝支付返回值特殊处理
				if (provider === "alipay") {
					if (res.orderInfo.code && res.orderInfo.code !== "10000") {
						res.code = res.orderInfo.code;
						res.msg = res.orderInfo.subMsg;
					}
				}
				// 生成二维码图片base64
				if (needQRcode === "image" && res.orderInfo.codeUrl) {
					res.qrcodeImage = await libs.qrcode.toDataURL(res.orderInfo.codeUrl, {
						type: "image/png",
						width: 200,
						margin: 1,
						scale: 1,
						color: {
							dark: "#000000",
							light: "#ffffff",
						},
						errorCorrectionLevel: "Q",
						quality: 1,
					});
				}
			}
		}
	} catch (e) {
		let errMsg = e.errorMessage || e.message;
		console.error("getOrderInfoParam: ", getOrderInfoParam);
		console.error("data: ", data);
		console.error("err: ", e);
		console.error("errMsg: ", errMsg);
		return new libs.error({ code: 3001, subMsg: errMsg });
	}
	let transaction_id =
		res.orderInfo && res.orderInfo.transaction_id
			? res.orderInfo.transaction_id
			: undefined;
	// 判断是否存在
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		// 添加数据库（数据库的out_trade_no字段需设置为唯一索引）
		let os = context.OS || context.os;
		let statPlatform = libs.common.getStatPlatform(context.PLATFORM, os);
		let uniPlatform = libs.common.getUniPlatform(context.uniPlatform);
		let extraData = {};
		if (transaction_id) {
			extraData = {
				transaction_id,
				transaction_ids: [transaction_id], // 储存所有的transaction_id（VksPay渠道需要）
			};
		}
		await dao.payOrders.add({
			status: 0,
			pid,
			app_auth_token,
			sub_appid,
			sub_mchid,
			appId: dcloudAppid,
			provider_appid: uniPayConifg.appId,
			mch_id: uniPayConifg.mchId,
			pay_type,
			provider_pay_method,
			platform: PLATFORM,
			uni_platform: uniPlatform,
			type,
			total_fee,
			out_trade_no,
			...extraData,
			description: subject,
			passback_params,
			create_date: nowTime,
			client_ip: CLIENTIP,
			device_id: deviceId,
			user_id,
			nickname, // 用户昵称冗余
			openid,
			...custom,
			notify_url: currentNotifyUrl,
			notify_path: currentNotifyPath,
			other,
			expand_data, // 微信虚拟支付扩展数据
			auth_code, // 付款码
			store_id, // 付款码机构id
			// 对接支付统计时需要
			stat_data: {
				platform: statPlatform,
				app_version: context.appVersion,
				app_version_code: context.appVersionCode,
				app_wgt_version: context.appWgtVersion,
				os: os,
				ua: context.ua,
				channel: context.channel ? context.channel : String(context.scene),
				scene: context.scene,
			},
		});
	} else {
		// 如果订单已经存在，则只修改订单支付方式（用户可能先点微信支付，未付款，又点了支付宝支付）
		if (payOrderInfo.status !== 0) {
			let statusObj = {
				"-1": "已关闭",
				1: "已支付",
				2: "已支付",
				3: "已退款",
			};
			let statusText =
				statusObj[String(payOrderInfo.status)] || "已支付或已关闭";
			return { code: -3, msg: `订单${statusText}，请勿重复支付` };
		}
		if (payOrderInfo.total_fee !== total_fee) {
			return {
				code: -1,
				msg: `订单金额不一致，请更换支付单号（out_trade_no）后重新发起`,
			};
		}
		if (payOrderInfo.pay_type.indexOf("wxpay-virtual_") === 0) {
			if (payOrderInfo.expand_data.mode !== expand_data.mode) {
				return {
					code: -1,
					msg: `订单类型不一致，请更换支付单号（out_trade_no）后重新发起`,
				};
			}
		}
		let extraData = {};
		if (transaction_id) {
			extraData = {
				transaction_id,
				transaction_ids: _.unshift(transaction_id), // 储存所有的transaction_id（VksPay渠道需要）
			};
		}
		await dao.payOrders.updateById(payOrderInfo._id, {
			...extraData,
			pay_type,
			provider_pay_method,
			openid,
		});
	}

	if (auth_code) {
		// 付款码支付需要先添加订单再执行，因为付款码支付触发免密支付时是实时支付
		res.orderInfo = await uniPayInstance.codepay(getOrderInfoParam);
	}

	// 如果配置了自动删除过期未支付的订单，则执行下面的逻辑
	if (config.autoDeleteExpiredOrders > 0) {
		// 利用随机数达到大概每20次请求会执行1次删除未付款订单。
		let deleteRandom = Math.floor(Math.random() * 20 + 1);
		if (deleteRandom === 6) {
			// 自动删除N天前的订单（未付款订单）
			await dao.payOrders.deleteExpPayOrders({
				day: config.autoDeleteExpiredOrders,
			});
		}
	}

	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 统一支付结果查询
 * @description 根据商户支付订单号或者平台订单号查询订单信息，主要用于未接收到支付通知时可以使用此接口进行支付结果验证
 * data 请求参数 说明
 * @param {String} out_trade_no 商户支付订单号
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.queryPayment = async (obj = {}) => {
	let {
		out_trade_no, // 商户支付订单号
		await_notify = false, // 是否需要等待异步通知执行完成才返回前端支付结果
		await_max_time = 20, // 最大等待时长，默认20秒（单位秒）
		pay_order_info = false, // 是否需要返回订单信息
	} = obj;
	let res = { code: 0, msg: "ok" };
	// 业务逻辑开始-----------------------------------------------------------
	if (!out_trade_no) {
		return new libs.error({ code: 1001 });
	}
	// 查询订单信息
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	// 初始化uniPayInstance
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	let orderQueryJson = {
		outTradeNo: out_trade_no,
	};
	if (payOrderInfo.pay_type.indexOf("wxpay-virtual_") === 0) {
		orderQueryJson.openid = payOrderInfo.openid;
	}
	if (uniPayInstance.custom) {
		orderQueryJson.payMethod = payOrderInfo.provider_pay_method;
		orderQueryJson.openid = payOrderInfo.openid;
	}
	// 查询是否已付款
	let queryResult;
	if (typeof uniPayInstance.orderQuery === "function") {
		queryResult = await uniPayInstance.orderQuery(orderQueryJson);
	} else {
		// 无uniPayInstance.orderQuery函数时的兼容处理
		if ([1, 2].indexOf(payOrderInfo.status) > -1) {
			queryResult = {
				tradeState: "SUCCESS",
				tradeStateDesc: "订单已支付",
			};
		} else if ([3].indexOf(payOrderInfo.status) > -1) {
			queryResult = {
				tradeState: "REFUNDED",
				tradeStateDesc: "订单已退款",
			};
		} else {
			queryResult = {
				tradeState: "NOPAY",
				tradeStateDesc: "订单未支付",
			};
		}
	}
	if (
		queryResult.tradeState === "SUCCESS" ||
		queryResult.tradeState === "FINISHED"
	) {
		// 如果已付款，再判断是否需要查询异步回调执行完毕
		if (typeof payOrderInfo.user_order_success == "undefined" && await_notify) {
			let whileTime = 0; // 当前循环已执行的时间（毫秒）
			let whileInterval = 500; // 每次循环间隔时间（毫秒）
			let maxTime = await_max_time * 1000; // 循环执行时间超过此值则退出循环（毫秒）
			while (
				typeof payOrderInfo.user_order_success == "undefined" &&
				whileTime <= maxTime
			) {
				await libs.common.sleep(whileInterval);
				whileTime += whileInterval;
				payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no); // 查询最新的订单信息
				// 如果等了500毫秒还没有回调过来，则主动调用支付成功统一处理函数
				if (whileTime === whileInterval && payOrderInfo.status === 0) {
					// 调用支付成功统一处理函数
					await vkPay.paymentSuccess({
						notify_mode: 0, // 通知模式：0 主动调用 1 异步回调
						type: payOrderInfo.type, // 订单类型
						out_trade_no: out_trade_no, // 商户支付订单号
						transaction_id: queryResult.transactionId, // 第三方支付单号
						// 以下是额外修改的数据（主动调用支付成功统一处理函数时，会丢失部分数据，如：notify_info和original_data
						extra_data: {
							openid: queryResult.openid,
						},
					});
					payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no); // 查询最新的订单信息
				}
			}
		}
		let notify_msg;
		if (typeof payOrderInfo.user_order_success === "undefined") {
			if (payOrderInfo.status === 0) {
				notify_msg = "回调待执行";
			} else {
				notify_msg = "回调进行中";
			}
		} else if (payOrderInfo.user_order_success) {
			notify_msg = "回调成功";
		} else if (payOrderInfo.user_order_success) {
			notify_msg = "回调失败";
		}
		if (payOrderInfo.notify_mode === 0) {
			notify_msg += "（主动回调）";
		}
		res = {
			code: 0,
			msg: "支付成功",
			notify_msg, // 回调是否成功的描述，仅描述用
			orderPaid: true, // 标记用户是否已付款成功（此参数只能表示用户确实付款了，但系统的异步回调逻辑可能还未执行完成）
			out_trade_no, // 商户支付订单号
			transaction_id: payOrderInfo.transaction_id, // 支付平台订单号
			status: payOrderInfo.status, // 标记当前支付订单状态 -1：已关闭 0：未支付 1：已支付 2：已部分退款 3：已全额退款
			user_order_success: payOrderInfo.user_order_success, // 用户异步通知逻辑是否全部执行完成，且无异常（建议前端通过此参数是否为true来判断是否支付成功）
			notify: payOrderInfo.user_order_success, // 兼容旧版 = user_order_success
		};
		if (pay_order_info) {
			res.payOrder = payOrderInfo;
		}
	} else {
		let errMsg = queryResult.tradeStateDesc;
		if (!errMsg) {
			if (payOrderInfo.status === 0) {
				errMsg = "订单未支付";
			} else if ([2, 3].indexOf(payOrderInfo.status) > -1) {
				errMsg = "订单已退款";
			} else {
				errMsg = "未支付或已退款";
			}
		}
		if (errMsg.indexOf("发生过退款") > -1) {
			errMsg = "订单已退款";
		}
		res = {
			code: -1,
			msg: errMsg,
			orderPaid: false,
			out_trade_no, // 商户支付订单号
			trade_state: queryResult.tradeState, // 交易状态
		};
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};
/**
 * 查询退款结果
 * @description 提交退款申请后，通过调用该接口查询退款状态。
 * data 请求参数 说明
 * @param {String} out_trade_no 商户支付订单号
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.queryRefund = async (obj = {}) => {
	let { out_trade_no } = obj;
	let res = { code: 0, msg: "ok" };
	if (!out_trade_no) {
		return new libs.error({ code: 1001 });
	}
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	if (payOrderInfo.status <= 0) {
		return new libs.error({ code: 2002 });
	}
	if (payOrderInfo.status === 1 && !payOrderInfo.refund_num) {
		return new libs.error({ code: 2003 });
	}
	const pay_type = payOrderInfo.pay_type;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	let queryResult;
	try {
		let refundQueryJson = {
			outTradeNo: out_trade_no,
			outRefundNo: payOrderInfo.refund_list[0].out_refund_no,
		};
		if (pay_type.indexOf("wxpay-virtual_") === 0) {
			refundQueryJson.openid = payOrderInfo.openid;
		}
		if (uniPayInstance.custom) {
			refundQueryJson.payMethod = payOrderInfo.provider_pay_method;
			refundQueryJson.openid = payOrderInfo.openid;
		}
		queryResult = await uniPayInstance.refundQuery(refundQueryJson);
	} catch (err) {
		return {
			code: -1,
			msg: "查询失败，请稍后再试",
			err: err,
			queryResult: queryResult,
		};
	}
	let orderInfo = {
		total_fee: payOrderInfo.total_fee,
		refund_fee: payOrderInfo.refund_fee,
		refund_num: payOrderInfo.refund_num,
		refund_list: payOrderInfo.refund_list,
		pay_type: payOrderInfo.pay_type,
		status: payOrderInfo.status,
		type: payOrderInfo.type,
		out_trade_no: payOrderInfo.out_trade_no,
		transaction_id: payOrderInfo.transaction_id,
	};
	if (queryResult.refundFee > 0) {
		let msg = "退款成功";
		if (payOrderInfo.refund_list && payOrderInfo.refund_list.length > 0) {
			msg = `合计退款 ${payOrderInfo.refund_fee / 100}\r\n`;
			for (let i in payOrderInfo.refund_list) {
				let item = payOrderInfo.refund_list[i];
				let index = Number(i) + 1;
				let timeStr = libs.common.timeFormat(
					item.refund_date || item.refund_time,
					"yyyy-MM-dd hh:mm:ss"
				);
				msg += `${index}、 ${timeStr} \r\n退款 ${item.refund_fee / 100} \r\n`;
			}
		}
		res = {
			code: 0,
			msg: msg,
			orderInfo: orderInfo,
			queryResult: queryResult,
		};
	} else {
		res = {
			code: -1,
			msg: "未退款",
			orderInfo: orderInfo,
			queryResult: queryResult,
		};
	}

	return res;
};
/**
 * 统一退款
 * @description 当交易发生之后一段时间内，由于买家或者卖家的原因需要退款时，卖家可以通过退款接口将支付款退还给买家。
 * data 请求参数 说明
 * @param {String} out_trade_no 商户支付订单号
 * @param {String} out_refund_no 商户退款单号（不填会自动生成）
 * @param {String} refund_desc 退款原因
 * @param {Number} refund_fee 退款总金额
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.refund = async (obj = {}) => {
	let {
		out_trade_no,
		out_refund_no,
		refund_desc = "用户申请退款",
		refund_fee: myRefundFee,
		refund_fee_type = "CNY",
	} = obj;

	let res = { code: 0, msg: "ok" };
	// 业务逻辑开始-----------------------------------------------------------
	if (!out_trade_no) {
		return new libs.error({ code: 1001 });
	}
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	if (payOrderInfo.status <= 0) {
		return new libs.error({ code: 2002 });
	}
	if (myRefundFee && typeof myRefundFee === "string") {
		myRefundFee = Number(myRefundFee);
	}
	// 如果该订单可能正在回调中，则60秒内不允许退款
	if (payOrderInfo.user_order_success === undefined) {
		let nowTime = Date.now();
		let payTime = payOrderInfo.pay_date;
		// 判断是否超支付时间60秒
		if (nowTime - payTime < 60000) {
			return new libs.error({ code: 2004 });
		}
	}

	let refund_num = payOrderInfo.refund_num || 0;
	refund_num++;
	// 生成退款订单号
	let outRefundNo = `${out_trade_no}-${refund_num}`;
	if (out_refund_no) {
		outRefundNo = out_refund_no;
	}
	// 订单总金额
	let totalFee = payOrderInfo.total_fee;
	// 退款总金额
	let refundFee = myRefundFee || totalFee;
	const pay_type = payOrderInfo.pay_type;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);

	// 退款操作
	try {
		let refundData = {
			outTradeNo: out_trade_no,
			outRefundNo,
			totalFee,
			refundFee,
			refundDesc: refund_desc,
			refundFeeType: refund_fee_type,
		};
		if (pay_type.indexOf("wxpay-virtual_") === 0) {
			// 微信小程序虚拟支付特殊参数
			refundData.openid = payOrderInfo.openid;
			refundData.refundReason = "3"; // 0-暂无描述 1-产品问题，影响使用或效果不佳 2-售后问题，无法满足需求 3-意愿问题，用户主动退款 4-价格问题 5-其他原因
			refundData.reqFrom = "2"; // 当前只支持"1"-人工客服退款，即用户电话给客服，由客服发起退款流程 "2"-用户自己发起退款流程 "3"-其他
			// 查询当前可退款金额
			refundData.leftFee = totalFee - (payOrderInfo.refund_fee || 0);
		} else if (
			pay_type.indexOf("douyin_") === 0 ||
			pay_type.indexOf("huawei_") === 0
		) {
			// 部分支付退款需要的特殊参数
			let finalNotifyUrl =
				payOrderInfo.notify_url +
				notifyPath +
				pay_type +
				"/" +
				payOrderInfo.out_trade_no;
			refundData.notifyUrl = finalNotifyUrl;
		}
		if (uniPayInstance.custom) {
			refundData.payMethod = payOrderInfo.provider_pay_method;
		}
		console.log(`执行退款请求开始`, refundData);
		console.log(
			`${out_trade_no} -- ${outRefundNo} -- ${totalFee / 100} -- ${
				refundFee / 100
			}`
		);

		res = await uniPayInstance.refund(refundData);

		console.log(`执行退款请求结束`, res);
	} catch (err) {
		if (
			err.message &&
			err.message.indexOf("unsupported") > -1 &&
			pay_type.indexOf("wxpay_") === 0
		) {
			return {
				code: -1,
				msg: "微信支付v2的退款不支持node18环境，请使用微信支付v3接口，详见文档：https://vkdoc.fsq.pub/vk-uni-pay/uniCloud/refund.html#微信支付v2退款报错说明",
			};
		}
		console.error("执行退款请求异常", err);
		return { code: -1, msg: err.message, err };
	}
	if (res.refundFee) {
		res.code = 0;
		res.msg = "退款成功";
		// 修改数据库
		try {
			let time = Date.now();
			// 修改订单状态
			console.log("修改订单状态开始", payOrderInfo._id);
			payOrderInfo = await dao.payOrders.updateAndReturn({
				whereJson: {
					_id: payOrderInfo._id,
					"refund_list.out_refund_no": _.neq(outRefundNo),
				},
				dataJson: {
					status: 2,
					refund_fee: _.inc(refundFee),
					refund_num: refund_num,
					refund_date: time, // 更新最近一次退款时间
					// 记录每次的退款详情
					refund_list: _.unshift({
						refund_date: time,
						refund_fee: refundFee,
						out_refund_no: outRefundNo,
						refund_id: res.refundId,
						refund_desc,
					}),
				},
			});
			console.log("修改订单状态结束", payOrderInfo ? "修改成功" : "修改失败");
			if (payOrderInfo && payOrderInfo.refund_fee >= payOrderInfo.total_fee) {
				// 修改订单状态为已全额退款
				await dao.payOrders.updateById(payOrderInfo._id, {
					status: 3,
					refund_fee: payOrderInfo.total_fee,
				});
				console.log("订单已全额退款");
			}
		} catch (err) {
			console.error("修改订单状态异常", err);
		}
	} else {
		res.code = -1;
		res.msg = res.subMsg || "退款失败";
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 关闭订单
 * @description 用于交易创建后，用户在一定时间内未进行支付，可调用该接口直接将未付款的交易进行关闭，避免重复支付。
 * 注意
 * 微信支付：订单生成后不能马上调用关单接口，最短调用时间间隔为 5 分钟。
 * 微信虚拟支付：不支持关闭订单
 * data 请求参数 说明
 * @param {String} out_trade_no 商户支付订单号
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.closeOrder = async (data = {}) => {
	let {
		out_trade_no, // 商户支付订单号
	} = data;
	if (!out_trade_no) return new libs.error({ code: 1001 });
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) return new libs.error({ code: 2001 });
	let { pay_type } = payOrderInfo;
	let uniPayInstance = await vkPay.initUniPayInstance(payOrderInfo);
	let isSuccess = false;
	let result = {};
	if (typeof uniPayInstance.closeOrder === "function") {
		let closeOrderData = {
			outTradeNo: out_trade_no,
		};
		if (uniPayInstance.custom) {
			closeOrderData.payMethod = payOrderInfo.provider_pay_method;
		}
		result = await uniPayInstance.closeOrder(closeOrderData);
		if (result.resultCode === "SUCCESS" || result.code === "10000") {
			isSuccess = true;
		}
	} else {
		isSuccess = true;
	}
	if (isSuccess) {
		// 修改订单状态为已取消
		await dao.payOrders.update({
			whereJson: {
				_id: payOrderInfo._id,
				status: 0,
			},
			dataJson: {
				status: -1,
				cancel_date: Date.now(),
			},
		});
		return {
			code: 0,
			msg: "订单已关闭",
			result: result,
		};
	} else {
		return {
			code: -1,
			msg: result.returnMsg || result.subMsg || result.msg,
			result: result,
		};
	}
};

/**
 * 转账到支付宝或微信零钱
 * @description 单笔转账接口是基于支付宝的资金处理能力
 * data 请求参数 说明详见：https://vkdoc.fsq.pub/vk-uni-pay/uniCloud/transfer.html
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.transfer = async (obj = {}) => {
	let { pay_type, provider, version, user_id } = obj;
	if (!provider && pay_type) provider = pay_type;
	// 如果设置了用户白名单，则只允许白名单中的用户调用此接口
	if (
		config.userWhitelist &&
		config.userWhitelist.length > 0 &&
		config.userWhitelist[0] &&
		config.userWhitelist.indexOf(user_id) === -1
	) {
		throw new Error("用户未授权调用此接口");
	}
	if (provider === "alipay") {
		return vkPay.alipay.transfer(obj);
	} else if (provider === "wxpay") {
		if (version === 3) {
			// 微信转账V3旧版，对应产品名称：商家转账到零钱，不推荐，后面微信官方会废弃此接口
			return vkPay.wxpay.transferOld(obj);
		} else {
			// 微信转账V3最新版，对应产品名称：商家转账，现在微信推荐使用此接口
			return vkPay.wxpay.transfer(obj);
		}
	} else {
		return { code: -1, msg: "不支持的支付方式" };
	}
};

/**
 * 转账结果异步通知
 */
vkPay.transferNotify = async (obj = {}) => {
	let { event, context, callback } = obj;
	let startTime = Date.now();
	console.log("原始回调数据: ", event);
	let notifyPathSplit = event.path
		.substring(transferNotifyPath.length)
		.split("/");
	// notifyPathSplit = "/wxpay_transfer/test1739351809934"
	let pay_type = notifyPathSplit[0]; // 获取支付方式
	let out_bill_no = notifyPathSplit[1]; // 获取商户转账订单号
	let pid = notifyPathSplit[2]; // pid
	let uniPayInstance = await vkPay.initUniPayInstance({ pay_type, pid });
	// 如果uniPayInstance中有returnNotifySuccess方法，则使用uniPayInstance中的方法，否则使用common中的方法
	let returnNotifySuccess =
		typeof uniPayInstance.returnNotifySuccess === "function"
			? uniPayInstance.returnNotifySuccess
			: libs.common.returnNotifySUCCESS;
	let verifyResult = await uniPayInstance.verifyTransferNotify(event);
	if (!verifyResult) {
		console.log("---------!签名验证未通过!---------");
		console.log("---------!签名验证未通过!---------");
		console.log("---------!签名验证未通过!---------");
		// 这里返回成功，是为了避免第三方支付服务器重复推送
		return returnNotifySuccess(pay_type);
	}
	console.log("---------!签名验证通过!---------");
	verifyResult = JSON.parse(JSON.stringify(verifyResult)); // 这一句代码有用，请勿删除。
	console.log("verifyResult: ", verifyResult);
	let { state, openid, mchId, outBillNo, transferBillNo, transferAmount } =
		verifyResult;

	let queryTransferRes = await vkPay.queryTransfer({
		provider: pay_type.split("_")[0],
		platform: pay_type.split("_")[1],
		out_bill_no: outBillNo,
	});

	console.log("转账结果: ", queryTransferRes.result);

	if (
		["SUCCESS", "FAIL", "CANCELLED"].indexOf(queryTransferRes.result.state) ===
		-1
	) {
		console.log(
			"---------!只有 state 为 SUCCESS 或者 FAIL 或者 CANCELLED 的时候才需要执行自定义转账结果回调函数!---------"
		);
		// 这里返回成功，是为了避免第三方支付服务器重复推送
		return returnNotifySuccess(pay_type);
	}

	console.log("用户自己的回调函数 - 开始执行");
	let user_order_success = await callback({
		data: queryTransferRes.result,
	});
	console.log("用户自己的回调函数 - 执行完成");
	console.log("user_order_success: ", user_order_success);

	// 给第三方服务器回复 开始-----------------------------------------------------------
	let endTime = Date.now();
	let runTime = endTime - startTime;
	console.log(`回调函数 - 全部执行完成，总耗时：${runTime}毫秒`);
	if (runTime > 4000) {
		console.log(
			"---------!您的回调执行时间过长，建议优化自定义回调逻辑，建议控制在4000毫秒以内---------!"
		);
		console.log(
			"---------!您的回调执行时间过长，建议优化自定义回调逻辑，建议控制在4000毫秒以内---------!"
		);
		console.log(
			"---------!您的回调执行时间过长，建议优化自定义回调逻辑，建议控制在4000毫秒以内---------!"
		);
	}
	return returnNotifySuccess(pay_type);
	// 给第三方服务器回复 结束-----------------------------------------------------------
};

/**
 * 商家转账 - 撤销转账
 * @description 仅支持微信支付
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.cancelTransfer = async (obj = {}) => {
	let { provider, version } = obj;
	if (provider === "alipay") {
		return { code: -1, msg: "不支持的支付方式" };
	} else if (provider === "wxpay") {
		return vkPay.wxpay.cancelTransfer(obj);
	} else {
		return { code: -1, msg: "不支持的支付方式" };
	}
};

/**
 * 商家转账 - 查询转账结果
 * @description 仅支持微信支付
 * data 请求参数 说明
 * @param {String} out_bill_no 转账单号（需保证唯一）
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.queryTransfer = async (obj = {}) => {
	let { provider, version } = obj;
	if (provider === "alipay") {
		return { code: -1, msg: "不支持的支付方式" };
	} else if (provider === "wxpay") {
		return vkPay.wxpay.queryTransfer(obj);
	} else {
		return { code: -1, msg: "不支持的支付方式" };
	}
};

/**
 * 商家转账 - 验证转账回调通知
 * @param {String} pid vk-pay-config表的_id（多商户模式下必填）
 * @param {String} provider 支付供应商，仅支持 wxpay：微信支付官方
 * @param {String} platform 平台类型，如：h5、h5-weixin、mp-weixin、app-plus
 * @param {String} headers 微信支付回调的请求头
 * @param {String} body 微信支付回调的body
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.verifyTransferNotify = async (data = {}) => {
	let { pid, provider, platform, pay_type, headers, body } = data;
	if (!pay_type) {
		platform = libs.common.getPlatform(platform);
		pay_type = `${provider}_${platform}`;
	}
	let uniPayInstance = await vkPay.initUniPayInstance({ pay_type, pid });
	let result = {};
	if (typeof uniPayInstance.verifyTransferNotify === "function") {
		result = await uniPayInstance.verifyTransferNotify({
			headers,
			body,
		});
	}
	return result;
};

/**
 * 查询支付商户里的余额
 * @description 查询支付商户里的余额
 * data 请求参数 说明
 * @param {String} provider 支付供应商 wxpay：微信支付官方 alipay：支付宝支付官方
 * @param {String} platform 平台类型，如：h5、h5-weixin、mp-weixin、app-plus
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.queryAccountBalance = async (obj = {}) => {
	obj.platform = libs.common.getPlatform(obj.platform);
	obj.pay_type = `${obj.provider}_${obj.platform}`;
	let uniPayInstance = await vkPay.initUniPayInstance(obj);
	if (typeof uniPayInstance.queryAccountBalance !== "function") {
		return { code: -1, msg: "不支持的支付方式" };
	}
	return await uniPayInstance.queryAccountBalance(obj);
};

/**
 * 获取对应支付配置
 * @param {String} pay_type 支付方式
 * @param {String} pid 商户id，若此参数有值，则会从数据库中获取支付配置进行支付
 * @param {String} app_auth_token 支付宝子商户授权token
 * @param {String} sub_appid 微信支付服务商模式下，子商户的appid，若该参数有值，自动视为微信支付服务商模式
 * @param {String} sub_mchid 微信支付服务商模式下，子商户的mchid，与sub_appid搭配使用
 * let uniPayConifg = await vkPay.getUniPayConfig({ pay_type, pid, app_auth_token, sub_appid, sub_mchid, provider_pay_method });
 */
vkPay.getUniPayConfig = async function (obj = {}) {
	let {
		pay_type,
		pid, // 商户id，若此参数有值，则会从数据库中获取支付配置进行支付
		app_auth_token,
		sub_appid,
		sub_mchid,
		provider_pay_method,
	} = obj;
	let arr = pay_type.split("_");
	let type1 = arr[0];
	let type2 = arr[1];
	if (!pid && !config.alipay && !config.wxpay && !config.vkspay) {
		throw new Error(`uni-pay配置${type1}.${type2}节点配置错误`);
	}
	let payConfig;
	if (pid) {
		// 从数据库中获取支付配置
		payConfig = await dao.payConfig.findById(pid);
	} else {
		// 从配置文件中获取支付配置
		payConfig = config;
	}
	if (!payConfig || !payConfig[type1]) {
		throw new Error(`uni-pay配置${type1}.${type2}节点配置错误`);
	}
	let uniPayConfig;
	if (type2) {
		if (type2.indexOf("mp-") > -1) {
			uniPayConfig = payConfig[type1][type2] || payConfig[type1]["mp"]; // 兼容mp、mp-weixin、mp-alipay
		} else if (type2.indexOf("app-") > -1) {
			uniPayConfig = payConfig[type1][type2] || payConfig[type1]["app"]; // 兼容app、app-plus
		} else if (["h5", "web", "native"].indexOf(type2) > -1) {
			uniPayConfig = payConfig[type1]["h5"] || payConfig[type1]["native"]; // 兼容web、h5、native
		} else if (["h5-weixin"].indexOf(type2) > -1) {
			uniPayConfig = payConfig[type1][type2] || payConfig[type1]["jsapi"]; // 兼容h5-weixin、jsapi
		} else {
			uniPayConfig = payConfig[type1][type2];
		}
	}
	if (type1 === "vkspay") {
		uniPayConfig = payConfig[type1];
		if (!uniPayConfig.mchId || !uniPayConfig.key) {
			throw new Error(`uni-pay配置${type1}节点下的mchId和key不能为空`);
		}
	}
	if (payConfig[type1].custom) {
		// 自定义渠道支付
		let customConfig = payConfig[type1];
		if (!provider_pay_method) {
			throw new Error(`参数provider_pay_method不能为空`);
		}
		let payMethodConfig = customConfig[provider_pay_method] || {};
		let platformConfig = payMethodConfig[type2] || {};
		uniPayConfig = Object.assign(
			{},
			customConfig,
			payMethodConfig,
			platformConfig
		);
		delete uniPayConfig[provider_pay_method];
	}
	if (!uniPayConfig) {
		throw new Error(`uni-pay配置${type1}.${type2}节点配置错误`);
	}
	if (type1 === "alipay" && app_auth_token) {
		// 支付宝服务商模式需要额外增加 appAuthToken 参数
		uniPayConfig = Object.assign({}, uniPayConfig, {
			appAuthToken: app_auth_token,
		});
	}
	if (type1 === "wxpay" && uniPayConfig.pfx) {
		// 微信支付证书 base64 转 buffer（数据库存的是 base64 格式的证书，而接口需要 buffer 格式的证书）
		uniPayConfig.pfx = Buffer.from(uniPayConfig.pfx, "base64");
	}
	if (type1 === "wxpay" && sub_mchid) {
		// 微信服务商模式需要额外增加 subAppId 和 subMchId 参数
		uniPayConfig = Object.assign({}, uniPayConfig, {
			subAppId: sub_appid,
			subMchId: sub_mchid,
		});
	}
	return uniPayConfig;
};
/**
 * 初始化uniPayInstance
 * @param {String} pay_type 支付方式
 * @param {String} app_auth_token 商户授权token
 * let uniPayInstance = await vkPay.initUniPayInstance({ pay_type, pid, app_auth_token, sub_appid, sub_mchid, provider_pay_method });
 * let uniPayInstance = await vkPay.initUniPayInstance(orderInfo);
 */
vkPay.initUniPayInstance = async function (obj = {}) {
	let { pay_type, version } = obj;
	let uniPayConifg = await vkPay.getUniPayConfig(obj);
	let uniPayInstance;
	if (pay_type.indexOf("wxpay_") == 0) {
		// 微信支付
		if (!version) version = uniPayConifg.version;
		if (version === 2) {
			// 微信支付V2
			uniPayInstance = uniPay.initWeixin(uniPayConifg);
		} else {
			// 微信支付V3
			uniPayInstance = uniPay.initWeixinV3(uniPayConifg);
		}
	} else if (pay_type.indexOf("alipay_") == 0) {
		// 支付宝
		uniPayInstance = uniPay.initAlipay(uniPayConifg);
	} else if (pay_type.indexOf("appleiap_") == 0) {
		// ios内购
		uniPayInstance = uniPay.initAppleIapPayment(uniPayConifg);
	} else if (pay_type.indexOf("vkspay_") == 0) {
		// VksPay支付
		uniPayInstance = new openapi.vkspay(uniPayConifg);
	} else if (pay_type.indexOf("wxpay-virtual_") == 0) {
		// 微信小程序虚拟支付
		// 获取accessToken
		let cacheKey = {
			appId: uniPayConifg.appId,
			platform: "weixin-mp",
		};
		let cacheInfo = await dao.opendbOpenData.getAccessToken(cacheKey);
		if (cacheInfo) {
			// 缓存有值
			uniPayConifg.accessToken = cacheInfo.access_token;
		} else {
			// 缓存无值
			let getAccessTokenRes = await vkPay.getAccessTokenWeixinMP(uniPayConifg);
			uniPayConifg.accessToken = getAccessTokenRes.accessToken;
			// 缓存accessToken
			await dao.opendbOpenData.setAccessToken(
				cacheKey,
				{
					access_token: getAccessTokenRes.accessToken,
				},
				getAccessTokenRes.expiresIn
			);
		}
		uniPayInstance = uniPay.initWeixinVirtualPayment(uniPayConifg);
	} else if (pay_type.indexOf("douyin_") == 0) {
		// 抖音支付
		uniPayInstance = uniPay.initDouyinPayment(uniPayConifg);
	} else if (pay_type.indexOf("huawei_") == 0) {
		// 华为支付
		uniPayInstance = uniPay.initHuawei(uniPayConifg);
	} else {
		let pay_type_arr = pay_type.split("_");
		let provider = pay_type_arr[0];
		let platform = pay_type_arr[1];
		uniPayInstance = await vkPay.initVkPayInstance({
			...obj,
			provider,
			platform,
		});
	}
	return uniPayInstance;
};
// 获取交易方式
vkPay.getTradeType = function (pay_type) {
	let pay_type_arr = pay_type.split("_");
	let provider = pay_type_arr[0];
	let platform = pay_type_arr[1];
	let obj = {
		"app-plus": "APP", // APP APP支付
		"mp-weixin": "JSAPI", // JSAPI 小程序支付
		"h5-weixin": "JSAPI", // JSAPI 公众号支付
		h5: "NATIVE", // NATIVE 网站二维码支付
		mweb: "MWEB", // MWEB 外部浏览器H5支付
		codepay: "CODEPAY", // CODEPAY 付款码支付
	};
	return obj[platform];
};
// 获取alipayAppPayToH5Pay
// await vkPay.getAlipayAppPayToH5Pay({ pid });
vkPay.getAlipayAppPayToH5Pay = async function (obj = {}) {
	const { alipayAppPayToH5Pay } = config;
	let { pid } = obj;
	let result = alipayAppPayToH5Pay;
	if (pid) {
		// 如果数据库内配置了alipayAppPayToH5Pay，则以数据库内的为准
		let payConfig = await dao.payConfig.findById(pid);
		if (typeof payConfig.alipayAppPayToH5Pay === "boolean") {
			result = payConfig.alipayAppPayToH5Pay;
		}
	}
	return result;
};

/**
 * 初始化vkPayInstance
 * @param {String} provider [必填]支付类型：wxpay（微信支付）、alipay（支付宝支付）
 * @param {String} provider_pay_method 渠道支付下的支付方式
 * @param {String} platform [微信支付必填] 平台类型，如：h5、h5-weixin、mp-weixin、app-plus
 * @param {Number} version 接口版本，默认2 可填2或3（仅微信支付使用）
 * @param {String} pid 商户id，若此参数有值，则会从数据库中获取支付配置
 * @param {String} app_auth_token [支付宝] 子商户授权token
 * @param {String} sub_appid 微信支付服务商模式下，子商户的appid，若该参数有值，自动视为微信支付服务商模式
 * @param {String} sub_mchid 微信支付服务商模式下，子商户的mchid，与sub_appid搭配使用
 * let vkPayInstance = await vkPay.initVkPayInstance(obj);
 */
vkPay.initVkPayInstance = async (obj = {}) => {
	let {
		provider,
		provider_pay_method,
		platform,
		version,
		pid,
		app_auth_token,
		sub_appid,
		sub_mchid,
	} = obj;

	if (platform == undefined) {
		throw new Error(`msg:platform不能为空`);
	}
	platform = libs.common.getPlatform(platform);
	// 获取支付配置
	let pay_type = `${provider}_${platform}`;
	let uniPayConifg = await vkPay.getUniPayConfig({ ...obj, pay_type });
	if (provider === "alipay") {
		// 支付宝
		return new openapi.alipay(uniPayConifg);
	} else if (provider === "wxpay") {
		if (!version) version = uniPayConifg.version || 2;
		if (version === 3) {
			// 微信支付v3版本
			return new openapi.wxpayV3(uniPayConifg);
		} else {
			// 微信支付v2版本
			return new openapi.wxpayV2(uniPayConifg);
		}
	} else {
		let OpenPayInstance = openapi[provider];
		if (!OpenPayInstance) {
			throw new Error(`msg:暂不支持${provider}`);
		}
		return new OpenPayInstance(uniPayConifg);
	}
};

////////////////////////////支付宝开始///////////////////////////////
vkPay.alipay = {};
/**
 * 转账到支付宝
 * data 请求参数
 * @param {String} pid vk-pay-config表的_id（多商户模式下必填）
 * @param {String} platform 使用哪个平台的配置，如 mp-weixin、h5-weixin 等，默认是 transfer
 * @param {String} out_bill_no 必填，商户系统内部的商家单号，要求此参数只能由数字、大小写字母组成，在商户系统内部唯一
 * @param {Number} transfer_amount 必填，转账金额 单位分 100=1元
 * @param {String} transfer_remark 转账备注
 * @param {String} payee_info 必填，收款方信息
 * @param {String} order_title 必填，转账业务的标题，用于在支付宝用户的账单里显示。
 * @param {Boolean} payer_use_alias 是否展示付款方别名，为true将展示商家支付宝在商家中心 商户信息 > 商户基本信息 页面配置的 商户别名
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.alipay.transfer = async (obj = {}) => {
	let {
		pid,
		platform = "transfer",
		payee_info,
		transfer_amount,
		out_bill_no,
		order_title,
		transfer_remark,
		app_auth_token,
		payer_use_alias = false,
	} = obj;
	platform = libs.common.getPlatform(platform);
	// 参数验证开始-----------------------------------------------------------
	if (!out_bill_no) return new libs.error({ code: 1021 });
	if (typeof transfer_amount !== "number" || transfer_amount <= 0)
		return new libs.error({ code: 1022 });
	if (!payee_info) return new libs.error({ code: 1025 });
	// 参数验证结束-----------------------------------------------------------
	// 业务逻辑开始-----------------------------------------------------------
	//if (!out_bill_no) out_bill_no = "ZZ" + libs.common.timeFormat(new Date(), "yyyyMMddhhmmssS") + libs.common.random(13);
	if (!order_title) {
		order_title = libs.common.timeFormat(new Date(), "yyyyMMdd") + "转账";
	}
	// 金额要/100，因为单位是分
	transfer_amount = parseFloat((transfer_amount / 100).toFixed(2));
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({
		pay_type: "alipay_" + platform,
		pid,
		app_auth_token,
	});
	// 请求参数
	let biz_content = {
		out_biz_no: out_bill_no,
		trans_amount: transfer_amount,
		order_title,
		product_code: "TRANS_ACCOUNT_NO_PWD",
		biz_scene: "DIRECT_TRANSFER",
		payee_info,
		remark: transfer_remark,
	};
	if (payer_use_alias) {
		biz_content.business_params = JSON.stringify({
			payer_show_name_use_alias: true,
		});
	}
	// 实例化
	let alipay = new openapi.alipay(uniPayConifg);
	let res = await alipay.transfer({
		biz_content,
		app_auth_token,
	});
	if (res.code === 0) {
		res.msg = "转账成功";
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

////////////////////////////支付宝结束///////////////////////////////
////////////////////////////微信支付开始///////////////////////////////

vkPay.wxpay = {};

/**
 * 微信转账V3旧版，对应产品名称：商家转账到零钱，不推荐
 * data 请求参数
 * @param {String} platform 若传了 platform，则appid = 配置中platform对应的appid
 * @param {String} appid 直接指定appid，若platform和appid均为空，则从配置中读取默认的appid
 * @param {String} openid 当前用户的openid
 * @param {String} real_name 收款人姓名
 * @param {Number} amount 转账金额 单位分 100=1元
 * @param {String} out_biz_no 转账单号
 * @param {String} title 转账标题
 * @param {String} remark 转账备注
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.wxpay.transferOld = async (obj = {}) => {
	let {
		platform: openidPlatform,
		appid,
		openid,
		real_name,
		amount,
		out_biz_no,
		title = "转账",
		remark = "转账",
		pid,
		transfer_detail_list,
	} = obj;
	let platform = "transfer";

	if (!platform) {
		return new libs.error({ code: 1011 });
	}
	platform = libs.common.getPlatform(platform);
	if (!out_biz_no)
		out_biz_no =
			"ZZ" +
			libs.common.timeFormat(new Date(), "yyyyMMddhhmmssS") +
			libs.common.random(13);
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({
		pay_type: "wxpay_" + platform,
		pid,
	});

	let requestData;
	if (transfer_detail_list) {
		// 批量转账
		requestData = {
			out_batch_no: out_biz_no,
			batch_name: obj.batch_name,
			batch_remark: obj.batch_remark,
			total_amount: obj.total_amount,
			total_num: transfer_detail_list.length,
			transfer_detail_list,
		};
	} else {
		// 单笔转账
		if (typeof amount !== "number" || amount <= 0) {
			return new libs.error({ code: 1008 });
		}
		if (!openid) return new libs.error({ code: 1012 });
		transfer_detail_list = [
			{
				out_detail_no: out_biz_no,
				transfer_amount: amount,
				transfer_remark: remark,
				openid,
				user_name: real_name,
			},
		];
		requestData = {
			out_batch_no: out_biz_no,
			batch_name: title,
			batch_remark: remark,
			total_amount: amount,
			total_num: transfer_detail_list.length,
			transfer_detail_list,
		};
	}
	let realityUniPayConifg = Object.assign({}, uniPayConifg);
	if (appid) {
		realityUniPayConifg.appId = appid;
	} else if (openidPlatform) {
		let openidPlatformUniPayConifg = await vkPay.getUniPayConfig({
			pay_type: "wxpay_" + openidPlatform,
			pid,
		});
		realityUniPayConifg.appId = openidPlatformUniPayConifg.appId;
	}
	// 实例化
	let wxPayV3 = new openapi.wxpayV3(realityUniPayConifg);
	let res = await wxPayV3.transferOld(requestData);
	if (res.code === 0) {
		res.msg = "转账申请已提交，请等待商家审核！";
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 微信转账V3最新版，对应产品名称：商家转账
 * data 请求参数
 * @param {String} pid vk-pay-config表的_id（多商户模式下必填）
 * @param {String} platform 使用哪个平台的配置，如 mp-weixin、h5-weixin 等，默认是 transfer
 * @param {String} out_bill_no 必填，商户系统内部的商家单号，要求此参数只能由数字、大小写字母组成，在商户系统内部唯一
 * @param {String} appid，不填默认使用配置中的appId
 * @param {String} openid 必填，收款用户的openid
 * @param {String} user_name 收款方真实姓名，转账金额 >= 2000元时必填
 * @param {Number} transfer_amount 必填，转账金额 单位分 100=1元
 * @param {String} transfer_remark 转账备注
 * @param {String} transfer_scene_id 必填，该笔转账使用的转账场景，可前往“商户平台-产品中心-商家转账”中申请。如：1001-现金营销
 * @param {String} user_recv_perception 用户收款时感知到的收款原因将根据转账场景自动展示默认内容。如有其他展示需求，可在本字段传入。各场景展示的默认内容和支持传入的内容 详见：https://pay.weixin.qq.com/doc/v3/merchant/4012711988#3.3-发起转账
 * @param {Array} transfer_scene_report_infos 必填，各转账场景下需报备的内容 详见：https://pay.weixin.qq.com/doc/v3/merchant/4012711988#（3）按转账场景报备背景信息
 * transfer_scene_report_infos 参数
 * @param {String} info_type 必填，请根据产品文档确认当前转账场景下需传入的信息类型，需按要求填入，有多个字段时需填写完整 如：转账场景为1000-现金营销，需填入活动名称、奖励说明
 * @param {String} info_content  必填，请根据信息类型，描述当前这笔转账单的转账背景 如：信息类型为活动名称，请在信息内容描述用户参与活动的名称，如新会员有礼。信息类型为奖励说明，请在信息内容描述用户因为什么奖励获取这笔资金，如注册会员抽奖一等奖
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.wxpay.transfer = async (obj = {}) => {
	let {
		pid,
		platform = "transfer",
		out_bill_no,
		appid, // appid，不填默认使用配置中的appid
		openid,
		user_name,
		transfer_amount,
		transfer_remark = "转账",
		notify_url,
		transfer_scene_id,
		user_recv_perception,
		transfer_scene_report_infos,
	} = obj;
	// 参数验证开始
	if (!platform) return new libs.error({ code: 1011 });
	if (!openid) return new libs.error({ code: 1012 });
	if (!out_bill_no) return new libs.error({ code: 1021 });
	if (typeof transfer_amount !== "number" || transfer_amount <= 0)
		return new libs.error({ code: 1022 });
	if (!transfer_scene_id) return new libs.error({ code: 1023 });
	if (!transfer_scene_report_infos) return new libs.error({ code: 1024 });
	// 参数验证结束
	// if (!out_bill_no) out_bill_no = "ZZ" + libs.common.timeFormat(new Date(), "yyyyMMddhhmmssS") + libs.common.random(13);
	platform = libs.common.getPlatform(platform);
	let pay_type = `wxpay_${platform}`;

	if (!notify_url) {
		// 拼接转账回调地址
		let cloudInfos = uniCloud.getCloudInfos()[0];
		let spaceId = cloudInfos.spaceId;
		let currentNotifyUrl = config.notifyUrl[spaceId];
		notify_url = `${currentNotifyUrl}${transferNotifyPath}${pay_type}/${out_bill_no}`;
		if (pid) {
			notify_url += `/${pid}`;
		}
		// 示例：https://fc-mp-33d35e24-c2f3-47b4-80fc-xxxxxxxx.next.bspapp.com/http/vk-pay/transfer-notify/wxpay_transfer/test1739351809934
	}

	let requestData = {
		appid,
		out_bill_no,
		openid,
		transfer_amount,
		transfer_remark,
		notify_url,
		transfer_scene_id,
		user_recv_perception,
		transfer_scene_report_infos,
	};
	if (transfer_amount >= 30) {
		// 大于0.3元的转账才能使用user_name参数
		requestData.user_name = user_name;
	}
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type, pid });
	// 实例化
	let wxPayV3 = new openapi.wxpayV3(uniPayConifg);
	let res = await wxPayV3.transfer(requestData);
	if (res.code === 0) {
		let stateObj = {
			ACCEPTED: "转账已受理",
			PROCESSING: "转账处理中",
			WAIT_USER_CONFIRM: "转账申请已提交，等待用户确认收款",
			TRANSFERING: "转账结果尚未明确",
			SUCCESS: "转账成功",
			FAIL: "转账失败",
			CANCELING: "商户撤销请求受理成功",
			CANCELLED: "转账撤销完成",
		};
		res.msg = stateObj[res.result.state] || "转账状态未知";
		if (["FAIL", "CANCELLED"].indexOf(res.result.state) > -1) {
			// 转账失败或撤销完成时，设置code为-1
			res.code = -1;
		}
		res.options = {
			mchId: res.result.mchId,
			appId: res.result.appId,
			package: res.result.package_info,
		};
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 商家转账撤销
 * data 请求参数
 * @param {String} pid vk-pay-config表的_id（多商户模式下必填）
 * @param {String} platform 使用哪个平台的配置，如 mp-weixin、h5-weixin 等
 * @param {String} out_bill_no 商家转账单号，与transfer_bill_no二选一
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.wxpay.cancelTransfer = async (obj = {}) => {
	let { pid, platform, out_bill_no } = obj;
	// 参数验证开始
	if (!platform) return new libs.error({ code: 1011 });
	if (!out_bill_no) return new libs.error({ code: 1021 });
	// 参数验证结束
	let requestData = {
		out_bill_no,
	};
	platform = libs.common.getPlatform(platform);
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({
		pay_type: `wxpay_${platform}`,
		pid,
	});
	// 实例化
	let wxPayV3 = new openapi.wxpayV3(uniPayConifg);
	let res = await wxPayV3.cancelTransfer(requestData);
	if (res.code === 0) {
		res.msg = "操作成功";
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 商家转账 - 查询转账结果
 * data 请求参数
 * @param {String} pid vk-pay-config表的_id（多商户模式下必填）
 * @param {String} platform 使用哪个平台的配置，如 mp-weixin、h5-weixin 等
 * @param {String} out_bill_no 商家转账单号，与transfer_bill_no二选一
 * @param {String} transfer_bill_no 微信转账单号，微信商家转账系统返回的唯一标识，与out_bill_no二选一
 * res 返回参数说明
 * @param {Number} code 错误码，0表示成功
 * @param {String} msg 详细信息
 */
vkPay.wxpay.queryTransfer = async (obj = {}) => {
	let { pid, platform, out_bill_no, transfer_bill_no } = obj;
	// 参数验证开始
	if (!platform) return new libs.error({ code: 1011 });
	if (!out_bill_no) return new libs.error({ code: 1021 });
	// 参数验证结束
	let requestData = {
		out_bill_no,
		transfer_bill_no,
	};
	platform = libs.common.getPlatform(platform);
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({
		pay_type: `wxpay_${platform}`,
		pid,
	});
	// 实例化
	let wxPayV3 = new openapi.wxpayV3(uniPayConifg);
	let res = await wxPayV3.queryTransfer(requestData);
	if (res.code === 0) {
		let stateObj = {
			ACCEPTED: "转账已受理",
			PROCESSING: "转账处理中",
			WAIT_USER_CONFIRM: "转账申请已提交，等待用户确认收款",
			TRANSFERING: "转账结果尚未明确",
			SUCCESS: "转账成功",
			FAIL: "转账失败",
			CANCELING: "商户撤销请求受理成功",
			CANCELLED: "转账撤销完成",
		};
		let state = res.result.state;
		res.msg = stateObj[state] || "转账状态未知";
	}
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

/**
 * 获取微信支付平台证书v3版本
 */
vkPay.wxpay.fetchCertificates = async (obj = {}) => {
	let res = { code: 0, msg: "ok" };
	let { pid, platform = "transfer" } = obj;

	if (platform == undefined) {
		return new libs.error({ code: 1011 });
	}
	platform = libs.common.getPlatform(platform);
	// 获取支付配置
	let uniPayConifg = await vkPay.getUniPayConfig({
		pay_type: "wxpay_" + platform,
		pid,
	});

	// 实例化
	let wxPayV3 = new openapi.wxpayV3(uniPayConifg);
	res = await wxPayV3.fetchCertificates();
	// 业务逻辑结束-----------------------------------------------------------
	return res;
};

////////////////////////////微信支付结束///////////////////////////////
/**
 * code换取openid(微信公众号)
 */
vkPay.code2SessionWeixinH5 = async (data = {}) => {
	let { code, pid } = data;
	if (!code) return new libs.error({ code: 1013 });
	// 获取公众号支付的配置信息
	let pay_type = "wxpay_h5-weixin";
	let config = await vkPay.getUniPayConfig({ pay_type, pid });
	let appId = config.subAppId || config.appId;
	let secret = config.subSecret || config.secret;
	if (!appId) return { code: -1, msg: `uni-pay配置${pay_type}的appId不能为空` };
	if (!secret)
		return { code: -1, msg: `uni-pay配置${pay_type}的secret不能为空` };
	let requestRes = await libs.common.request({
		url: `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${secret}&code=${code}&grant_type=authorization_code`,
	});
	if (requestRes.errcode) {
		let msg = requestRes.errmsg;
		if (requestRes.errcode === 40163) msg = "该code已被使用，请重新获取";
		return {
			...requestRes,
			code: requestRes.errcode,
			msg,
		};
	}
	// 转驼峰
	requestRes = libs.common.snake2camelJson(requestRes);
	return {
		...requestRes,
		code: 0,
		msg: "ok",
	};
};

/**
 * code换取openid(微信小程序)
 */
vkPay.code2SessionWeixinMP = async (data = {}) => {
	let { code, appid, pid } = data;
	if (!code) return new libs.error({ code: 1013 });
	// 获取微信小程序支付的配置信息
	let pay_type = "wxpay_mp-weixin";
	let config = {};
	let needCacheSessionKey = false;
	try {
		config = await vkPay.getUniPayConfig({ pay_type, pid });
	} catch (err) {
		config = {};
	}
	try {
		// 如果配置了微信虚拟支付，则使用微信虚拟支付的配置作为微信获取openid的配置
		let wxpayVirtualPayConifg = await vkPay.getUniPayConfig({
			pay_type: "wxpay-virtual_mp-weixin",
		});
		if (
			wxpayVirtualPayConifg &&
			wxpayVirtualPayConifg.appId &&
			wxpayVirtualPayConifg.secret
		) {
			if (!appid || appid === wxpayVirtualPayConifg.appId) {
				config = wxpayVirtualPayConifg;
				needCacheSessionKey = true;
			}
		}
	} catch (err) {}
	let appId = config.subAppId || config.appId;
	let secret = config.subSecret || config.secret;
	if (!appId || !secret) {
		// 如果配置了VksPay支付，则不报这个错误，因为VksPay支付不需要获取openid
		let vkspayConfig;
		try {
			vkspayConfig = await vkPay.getUniPayConfig({ pay_type: "vkspay", pid });
		} catch (err) {
			vkspayConfig = {};
		}
		if (vkspayConfig && vkspayConfig.mchId) {
			return { code: 0 };
		} else {
			if (!appId)
				return { code: -1, msg: `uni-pay配置${pay_type}的appId不能为空` };
			if (!secret)
				return { code: -1, msg: `uni-pay配置${pay_type}的secret不能为空` };
		}
	}
	let requestRes = await libs.common.request({
		url: `https://api.weixin.qq.com/sns/jscode2session?appid=${appId}&secret=${secret}&js_code=${code}&grant_type=authorization_code`,
	});
	if (requestRes.errcode) {
		let msg = requestRes.errmsg;
		let errcodeObj = {
			40163: "该code已被使用，请重新获取",
			40029: `code无效，可能是支付配置里的appId和manifest.json下的小程序appId不匹配`,
			40125: `支付配置里的secret和appId不匹配`,
			40013: `支付配置里的appId无效`,
		};
		if (errcodeObj[requestRes.errcode + ""])
			msg = errcodeObj[requestRes.errcode + ""];
		return {
			...requestRes,
			code: requestRes.errcode,
			msg,
		};
	}
	// 转驼峰
	requestRes = libs.common.snake2camelJson(requestRes);
	console.log("requestRes: ", requestRes);
	// 缓存sessionKey
	if (needCacheSessionKey && requestRes.sessionKey) {
		let cacheKey = {
			appId: config.appId,
			platform: "weixin-mp",
			openid: requestRes.openid,
		};
		await dao.opendbOpenData.setSessionKey(
			cacheKey,
			{ session_key: requestRes.sessionKey },
			30 * 24 * 60 * 60
		);
	}
	return {
		...requestRes,
		code: 0,
		msg: "ok",
	};
};

/**
 * 获取微信小程序的accessToken
 */
vkPay.getAccessTokenWeixinMP = async (data = {}) => {
	let { appId, secret, force_refresh } = data;
	let res = await uniCloud.httpclient.request(
		"https://api.weixin.qq.com/cgi-bin/stable_token",
		{
			method: "POST",
			data: {
				appid: appId,
				secret: secret,
				grant_type: "client_credential",
				force_refresh,
			},
			contentType: "json",
			dataType: "json",
		}
	);
	let result = res.data || {};
	if (!result.access_token) {
		return {
			code: -1,
			msg: result.errmsg,
		};
	}
	return {
		code: 0,
		msg: "ok",
		accessToken: result.access_token,
		expiresIn: result.expires_in,
	};
};

/**
 * code换取openid(支付宝小程序)
 */
vkPay.code2SessionAlipay = async (data = {}) => {
	let { code, app_auth_token, pid } = data;
	if (!code) return new libs.error({ code: 1013 });
	// 获取支付宝小程序支付的配置信息
	let pay_type = "alipay_mp-alipay";
	let config = {};
	try {
		config = await vkPay.getUniPayConfig({ pay_type, pid, app_auth_token });
	} catch (err) {
		config = {};
	}
	let appId = config.appId;
	let privateKey = config.privateKey;
	// 如果配置了VksPay支付，则不报这个错误，因为VksPay支付不需要获取openid
	if (!appId || !privateKey) {
		let vkspayConfig;
		try {
			vkspayConfig = await vkPay.getUniPayConfig({ pay_type: "vkspay", pid });
		} catch (err) {
			vkspayConfig = {};
		}
		if (vkspayConfig && vkspayConfig.mchId) {
			return { code: 0 };
		} else {
			if (!appId)
				return { code: -1, msg: `uni-pay配置${pay_type}的appId不能为空` };
			if (!privateKey)
				return { code: -1, msg: `uni-pay配置${pay_type}的privateKey不能为空` };
		}
	}
	// 实例化
	let alipay = new openapi.alipay(config);
	let res = await alipay.code2SessionAlipay({ code });
	if (res.code !== 0) {
		return res;
	}
	return {
		code: 0,
		msg: "ok",
		openid: res.result.user_id,
		user_id: res.result.user_id,
	};
};

vkPay.code2SessionWeixinAlipay = vkPay.code2SessionAlipay; // 兼容旧版

/**
 * 解析token并返回user_id，解析失败不会报错，会返回 undefined
 */
vkPay.checkTokenReturnUserId = async (obj = {}) => {
	let { uniIdToken, context } = obj;
	let user_id;
	if (!uniIdToken) {
		return undefined;
	}
	try {
		// 只是尝试解析token，如果解析失败，则直接返回 undefined
		let uniId = libs.uniIdToken.createInstance({ context });
		let tokenRes = await uniId.checkToken(uniIdToken, { autoRefresh: false });
		if (tokenRes.uid) user_id = tokenRes.uid;
	} catch (err) {
		return undefined;
	}
	return user_id;
};

/**
 * 根据user_id获取nickname
 */
vkPay.getUserNickname = async (user_id) => {
	let nickname;
	if (!user_id) return nickname;
	let userInfo = await dao.uniIdUsers.findById(user_id);
	if (userInfo && userInfo.nickname) {
		nickname = userInfo.nickname;
	}
	return nickname;
};

// ios内购相关
vkPay.appleiap = {};
/**
 * 验证ios凭据（ios没有异步回调，需要主动回调）
 */
vkPay.appleiap.verifyReceipt = async (obj = {}) => {
	let {
		out_trade_no,
		transaction_receipt,
		transaction_identifier,
		pid,
		pay_type,
	} = obj;
	if (!out_trade_no || typeof out_trade_no !== "string") {
		return new libs.error({ code: 1001 });
	}
	if (!transaction_receipt || typeof transaction_receipt !== "string") {
		return new libs.error({ code: 1014 });
	}
	if (!transaction_identifier || typeof transaction_identifier !== "string") {
		return new libs.error({ code: 1015 });
	}
	let payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	if (!payOrderInfo) {
		return new libs.error({ code: 2001 });
	}
	if (!pid) pid = payOrderInfo.pid;
	if (!pay_type || pay_type.indexOf("appleiap_") !== 0)
		pay_type = `appleiap_app-plus`;
	// 初始化uniPayInstance
	let uniPayConifg = await vkPay.getUniPayConfig({ pay_type, pid });
	let uniPayInstance = await vkPay.initUniPayInstance({ pay_type, pid });
	let verifyReceiptRes = await uniPayInstance.verifyReceipt({
		receiptData: transaction_receipt,
	});
	if (verifyReceiptRes.tradeState !== "SUCCESS") {
		// 尝试使用相反的环境再次验证
		uniPayInstance = uniPay.initAppleIapPayment({
			...uniPayConifg,
			sandbox: !uniPayConifg.sandbox,
		});
		verifyReceiptRes = await uniPayInstance.verifyReceipt({
			receiptData: transaction_receipt,
		});
		if (verifyReceiptRes.tradeState !== "SUCCESS") {
			// 如果还是不成功，则返回校验不通过
			return new libs.error({ code: 2002 });
		}
	}
	// 支付成功
	let pay_date = Number(verifyReceiptRes.receipt.receipt_creation_date_ms); // 获取支付时间
	let inAppList = verifyReceiptRes.receipt.in_app || [];
	let inApp = inAppList.find((item) => {
		return item.transaction_id === transaction_identifier;
	});
	if (!inApp) {
		// 校验不通过
		return new libs.error({ code: 1016 });
	}

	let {
		quantity, // 购买数量
		product_id, // 对应的内购产品id
		transaction_id, // 第三方交易单号
	} = inApp;

	let receiptExpiresIn = uniPayConifg.receiptExpiresIn || 86400;
	if (Date.now() - receiptExpiresIn * 1000 > pay_date) {
		return { code: 0, msg: "凭据已失效" };
	}
	// 查询该transaction_id是否使用过，如果已使用，则不做处理，通知前端直接关闭订单。
	let queryInfo = await dao.payOrders.find({
		transaction_id,
	});
	if (queryInfo) return { code: 0, msg: "凭据已被使用过" };
	// 调用支付成功统一处理函数
	await vkPay.paymentSuccess({
		notify_mode: 0, // 通知模式：0 主动调用 1 异步回调
		type: payOrderInfo.type,
		out_trade_no: out_trade_no, // 商户支付订单号
		transaction_id: transaction_id, // 第三方支付单号
		// 以下是额外修改的数据
		extra_data: {
			original_data: obj, // 存本次的回调原始数据（为了留个备份，方便核查数据）
		},
	});
	payOrderInfo = await dao.payOrders.findByOutTradeNo(out_trade_no);
	return {
		code: 0,
		msg: "支付成功",
		orderPaid: true, // 标记用户是否已付款成功（此参数只能表示用户确实付款了，但系统的异步回调逻辑可能还未执行完成）
		out_trade_no, // 商户支付订单号
		transaction_id, // 支付平台订单号
		status: payOrderInfo.status, // 标记当前支付订单状态 -1：已关闭 0：未支付 1：已支付 2：已部分退款 3：已全额退款
		user_order_success: payOrderInfo.user_order_success, // 用户异步通知逻辑是否全部执行完成，且无异常（建议前端通过此参数是否为true来判断是否支付成功）
		notify: payOrderInfo.user_order_success, // 兼容旧版 = user_order_success
		payOrder: payOrderInfo,
	};
};

/**
 * 获取微信虚拟支付实例对象
 * pid 商户id，若此参数有值，则会从数据库中获取支付配置进行支付
 */
vkPay.getWxpayVirtualManager = async (obj = {}) => {
	let pay_type = "wxpay-virtual_mp-weixin";
	obj.pay_type = pay_type;
	let uniPayInstance = await vkPay.initUniPayInstance(obj);
	uniPayInstance.getSessionKey = async (openid) => {
		let uniPayConifg = await vkPay.getUniPayConfig(obj);
		if (!openid) return new libs.error({ code: 1012 });
		let cacheKey = {
			appId: uniPayConifg.appId,
			platform: "weixin-mp",
			openid: openid,
		};
		let { session_key } = await dao.opendbOpenData.getSessionKey(cacheKey);
		return session_key;
	};
	return uniPayInstance;
};

module.exports = vkPay;
